/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package net.sf.l2j.gameserver.instancemanager;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Logger;

import javolution.util.FastList;
import javolution.util.FastMap;
import net.sf.l2j.database.L2DatabaseFactory;
import net.sf.l2j.gameserver.datatables.SkillTable;
import net.sf.l2j.gameserver.model.L2Character;
import net.sf.l2j.gameserver.model.L2Clan;
import net.sf.l2j.gameserver.model.L2Object;
import net.sf.l2j.gameserver.model.Location;
import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
import net.sf.l2j.gameserver.model.entity.Fort;
import net.sf.l2j.gameserver.model.entity.FortSiege;
import net.sf.l2j.gameserver.network.SystemMessageId;
import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
import net.sf.l2j.gameserver.network.serverpackets.UserInfo;

public class FortSiegeManager {
	private static final Logger _log = Logger.getLogger(FortSiegeManager.class
			.getName());
	public static final String FORTSIEGE_CONFIGURATION_FILE = "./config/fort_siege.properties";

	public static FortSiegeManager getInstance() {
		return SingletonHolder._instance;
	}

	// =========================================================
	// Data Field
	private int _attackerMaxClans = 500; // Max number of clans
	private int _attackerRespawnDelay = 20000; // Time in ms. Changeable in
	// siege.config
	private int _defenderMaxClans = 500; // Max number of clans
	private int _defenderRespawnDelay = 10000; // Time in ms. Changeable in
	// siege.config
	// Siege settings
	private FastMap<Integer, FastList<SiegeSpawn>> _artefactSpawnList;
	private FastMap<Integer, FastList<SiegeSpawn>> _controlTowerSpawnList;
	private int _controlTowerLosePenalty = 20000; // Time in ms.
	// Changeable in
	// siege.config
	private int _flagMaxCount = 1; // Changeable in siege.config
	private int _siegeClanMinLevel = 4; // Changeable in siege.config
	private int _siegeLength = 120; // Time in minute. Changeable in
	// siege.config
	private List<FortSiege> _sieges;

	// =========================================================
	// Constructor
	private FortSiegeManager() {
		System.out.println("Initializing FortSiegeManager");
		load();
	}

	// =========================================================
	// Method - Public
	public final void addSiegeSkills(L2PcInstance character) {
		character.addSkill(SkillTable.getInstance().getInfo(246, 1), false);
		character.addSkill(SkillTable.getInstance().getInfo(247, 1), false);
	}

	/**
	 * Return true if character summon<BR>
	 * <BR>
	 * 
	 * @param activeChar
	 *            The L2Character of the character can summon
	 */
	public final boolean checkIfOkToSummon(L2Character activeChar,
			boolean isCheckOnly) {
		if (activeChar == null || !(activeChar instanceof L2PcInstance))
			return false;
		SystemMessage sm = new SystemMessage(SystemMessageId.S1_S2);
		L2PcInstance player = (L2PcInstance) activeChar;
		Fort fort = FortManager.getInstance().getFort(player);
		if (fort == null || fort.getFortId() <= 0)
			sm.addString("You must be on fort ground to summon this");
		else if (!fort.getSiege().getIsInProgress())
			sm.addString("You can only summon this during a siege.");
		else if (player.getClanId() != 0
				&& fort.getSiege().getAttackerClan(player.getClanId()) == null)
			sm.addString("You can only summon this as a registered attacker.");
		else
			return true;
		if (!isCheckOnly)
			player.sendPacket(sm);
		return false;
	}

	/**
	 * Return true if the clan is registered or owner of a fort<BR>
	 * <BR>
	 * 
	 * @param clan
	 *            The L2Clan of the player
	 */
	public final boolean checkIsRegistered(L2Clan clan, int fortid) {
		if (clan == null)
			return false;
		if (clan.getHasFort() > 0)
			return true;
		Connection con = null;
		boolean register = false;
		try {
			con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con
					.prepareStatement("SELECT clan_id FROM fortsiege_clans where clan_id=?");
			statement.setInt(1, clan.getClanId());
			ResultSet rs = statement.executeQuery();
			while (rs.next()) {
				register = true;
				break;
			}
			rs.close();
			statement.close();
		} catch (Exception e) {
			System.out.println("Exception: checkIsRegistered(): "
					+ e.getMessage());
			e.printStackTrace();
		} finally {
			try {
				con.close();
			} catch (Exception e) {
			}
		}
		return register;
	}

	public final void removeSiegeSkills(L2PcInstance character) {
		character.removeSkill(SkillTable.getInstance().getInfo(246, 1));
		character.removeSkill(SkillTable.getInstance().getInfo(247, 1));
	}

	// =========================================================
	// Method - Private
	private final void load() {
		try {
			InputStream is = new FileInputStream(new File(
					FORTSIEGE_CONFIGURATION_FILE));
			Properties siegeSettings = new Properties();
			siegeSettings.load(is);
			is.close();
			// Siege setting
			_attackerMaxClans = Integer.decode(siegeSettings.getProperty(
					"AttackerMaxClans", "500"));
			_attackerRespawnDelay = Integer.decode(siegeSettings.getProperty(
					"AttackerRespawn", "30000"));
			_controlTowerLosePenalty = Integer.decode(siegeSettings
					.getProperty("CTLossPenalty", "20000"));
			_defenderMaxClans = Integer.decode(siegeSettings.getProperty(
					"DefenderMaxClans", "500"));
			_defenderRespawnDelay = Integer.decode(siegeSettings.getProperty(
					"DefenderRespawn", "20000"));
			_flagMaxCount = Integer.decode(siegeSettings.getProperty(
					"MaxFlags", "1"));
			_siegeClanMinLevel = Integer.decode(siegeSettings.getProperty(
					"SiegeClanMinLevel", "4"));
			_siegeLength = Integer.decode(siegeSettings.getProperty(
					"SiegeLength", "60"));
			// Siege spawns settings
			_controlTowerSpawnList = new FastMap<Integer, FastList<SiegeSpawn>>();
			_artefactSpawnList = new FastMap<Integer, FastList<SiegeSpawn>>();
			for (Fort fort : FortManager.getInstance().getForts()) {
				fort.getSiege();
				FastList<SiegeSpawn> _controlTowersSpawns = new FastList<SiegeSpawn>();
				for (int i = 1; i < 0xFF; i++) {
					String _spawnParams = siegeSettings.getProperty(
							fort.getName() + "ControlTower"
									+ Integer.toString(i), "");
					if (_spawnParams.length() == 0)
						break;
					StringTokenizer st = new StringTokenizer(
							_spawnParams.trim(), ",");
					try {
						int x = Integer.parseInt(st.nextToken());
						int y = Integer.parseInt(st.nextToken());
						int z = Integer.parseInt(st.nextToken());
						int npc_id = Integer.parseInt(st.nextToken());
						int hp = Integer.parseInt(st.nextToken());
						_controlTowersSpawns.add(new SiegeSpawn(fort
								.getFortId(), x, y, z, 0, npc_id, hp));
					} catch (Exception e) {
						_log.warning("Error while loading control tower(s) for "
								+ fort.getName() + " fort.");
					}
				}
				FastList<SiegeSpawn> _artefactSpawns = new FastList<SiegeSpawn>();
				for (int i = 1; i < 0xFF; i++) {
					String _spawnParams = siegeSettings.getProperty(
							fort.getName() + "Artefact" + Integer.toString(i),
							"");
					if (_spawnParams.length() == 0)
						break;
					StringTokenizer st = new StringTokenizer(
							_spawnParams.trim(), ",");
					try {
						int x = Integer.parseInt(st.nextToken());
						int y = Integer.parseInt(st.nextToken());
						int z = Integer.parseInt(st.nextToken());
						int heading = Integer.parseInt(st.nextToken());
						int npc_id = Integer.parseInt(st.nextToken());
						_artefactSpawns.add(new SiegeSpawn(fort.getFortId(), x,
								y, z, heading, npc_id));
					} catch (Exception e) {
						_log.warning("Error while loading artefact(s) for "
								+ fort.getName() + " fort.");
					}
				}
				_controlTowerSpawnList.put(fort.getFortId(),
						_controlTowersSpawns);
				_artefactSpawnList.put(fort.getFortId(), _artefactSpawns);
			}
		} catch (Exception e) {
			// _initialized = false;
			System.err.println("Error while loading fort siege data.");
			e.printStackTrace();
		}
	}

	// =========================================================
	// Property - Public
	public final FastList<SiegeSpawn> getArtefactSpawnList(int _fortId) {
		if (_artefactSpawnList.containsKey(_fortId))
			return _artefactSpawnList.get(_fortId);
		else
			return null;
	}

	public final FastList<SiegeSpawn> getControlTowerSpawnList(int _fortId) {
		if (_controlTowerSpawnList.containsKey(_fortId))
			return _controlTowerSpawnList.get(_fortId);
		else
			return null;
	}

	public final int getAttackerMaxClans() {
		return _attackerMaxClans;
	}

	public final int getAttackerRespawnDelay() {
		return _attackerRespawnDelay;
	}

	public final int getControlTowerLosePenalty() {
		return _controlTowerLosePenalty;
	}

	public final int getDefenderMaxClans() {
		return _defenderMaxClans;
	}

	public final int getDefenderRespawnDelay() {
		return _defenderRespawnDelay;
	}

	public final int getFlagMaxCount() {
		return _flagMaxCount;
	}

	public final FortSiege getSiege(L2Object activeObject) {
		return getSiege(activeObject.getX(), activeObject.getY(),
				activeObject.getZ());
	}

	public final FortSiege getSiege(int x, int y, int z) {
		for (Fort fort : FortManager.getInstance().getForts())
			if (fort.getSiege().checkIfInZone(x, y, z))
				return fort.getSiege();
		return null;
	}

	public final int getSiegeClanMinLevel() {
		return _siegeClanMinLevel;
	}

	public final int getSiegeLength() {
		return _siegeLength;
	}

	public final List<FortSiege> getSieges() {
		if (_sieges == null)
			_sieges = new FastList<FortSiege>();
		return _sieges;
	}

	public final void addSiege(FortSiege fortSiege) {
		if (_sieges == null)
			_sieges = new FastList<FortSiege>();
		_sieges.add(fortSiege);
	}

	public final void removeSiege(FortSiege fortSiege) {
		if (_sieges == null)
			_sieges = new FastList<FortSiege>();
		_sieges.remove(fortSiege);
	}

	public final void onEnterWorld(L2PcInstance activeChar) {
		for (FortSiege siege : getSieges()) {
			if (!siege.getIsInProgress())
				continue;
			if (siege.checkIsAttacker(activeChar.getClan())) {
				activeChar.setSiegeState((byte) 1);
				activeChar.sendPacket(new UserInfo(activeChar));
			} else if (siege.checkIsDefender(activeChar.getClan())) {
				activeChar.setSiegeState((byte) 2);
				activeChar.sendPacket(new UserInfo(activeChar));
			}
		}
	}

	public class SiegeSpawn {
		Location _location;
		private int _npcId;
		private int _heading;
		private int _fortId;
		private int _hp;

		public SiegeSpawn(int fort_id, int x, int y, int z, int heading,
				int npc_id) {
			_fortId = fort_id;
			_location = new Location(x, y, z, heading);
			_heading = heading;
			_npcId = npc_id;
		}

		public SiegeSpawn(int fort_id, int x, int y, int z, int heading,
				int npc_id, int hp) {
			_fortId = fort_id;
			_location = new Location(x, y, z, heading);
			_heading = heading;
			_npcId = npc_id;
			_hp = hp;
		}

		public int getFortId() {
			return _fortId;
		}

		public int getNpcId() {
			return _npcId;
		}

		public int getHeading() {
			return _heading;
		}

		public int getHp() {
			return _hp;
		}

		public Location getLocation() {
			return _location;
		}
	}

	@SuppressWarnings("synthetic-access")
	private static class SingletonHolder {
		protected static final FortSiegeManager _instance = new FortSiegeManager();
	}
}
